package waal.pc.frontend;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.DatagramSocket;
import java.net.SocketTimeoutException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TimerTask;

import waal.pc.backend.Log;
import waal.pc.backend.hub.Hub;


public class ScanForHubs extends TimerTask implements Runnable {

	private static boolean running = false;
	private Surface surface;
	private DatagramSocket socket;
	private Set<Hub> oldHubs;
	private Map<InetAddress, Hub> hubAddresses;
	private Set<Hub> foundHubs;
	private boolean checkOK; 
		
	public ScanForHubs(Surface surface) {
		this.surface = surface;
		this.hubAddresses = new HashMap<InetAddress, Hub>();
		this.foundHubs = new  HashSet<Hub>();
	}
	
	public void  run() {
		if(ScanForHubs.running) {
			return;
		} else {
			ScanForHubs.running = true;
		}
		this.fillAddressesMap();
		this.sentRequest();
		byte[] data;
		int[] answer;
		DatagramPacket p;
		try {
			while( true ) {
				while( true ) {
					data = new byte[1365];
					p = new DatagramPacket(data, data.length);
					this.socket.receive(p);
					if((data[0] & 0xFF) == 0xFF) {
						answer = this.calculateChecksum(p.getData(), p.getLength());
						if(this.checkOK) this.processHubAnswer(p.getAddress(), answer);
					}
				}
			}
		} catch (SocketTimeoutException e) {
			//time is over now
		} catch (IOException e) {
			Log.addToLog("Fehler beim emfangen von config Daten. ScanForHub");
		} finally {
			this.cleanUp();
			this.socket.close();
		}
		ScanForHubs.running = false;
		//System.out.println("Stop");
	}
	
	private void sentRequest() {
		byte[] data = new byte[1]; 
		data[0] = (byte) 0xFF;
		try {
			DatagramPacket p = new DatagramPacket(data, data.length,InetAddress.getByName("192.168.0.255"),3142 );
			//DatagramPacket p = new DatagramPacket(data, data.length,InetAddress.getByName("130.75.114.127"),3142 );
			this.socket = new DatagramSocket();
			this.socket.setBroadcast(true);
			//this.debugPacket(p);
			this.socket.send(p);
			//System.out.println("Habe gesendet");
			this.socket.setSoTimeout(1000);
		} catch (IOException e) {
			Log.addToLog("Fehler beim senden von broadcast Daten. ScanForHub");
		}
	}
	
	private void fillAddressesMap() {
		this.oldHubs =  new HashSet<Hub>(this.surface.getHubs());
		this.hubAddresses.clear();
		this.foundHubs.clear();
		Iterator<Hub> i = this.oldHubs.iterator();
		Hub hub;
		while(i.hasNext()) {
			hub = i.next();
			this.hubAddresses.put(hub.getAddress(), hub);
		}
	}
	
	private void processHubAnswer(InetAddress address, int[] data) {
		if(data.length != 3) {
			System.out.println("Bad Hub");
			return;
		}
		
		//InetAddress ip = Utility.getIP(data[1], data[2], data[3], data[4]);
		
		Hub hub;
		if(this.hubAddresses.containsKey(address)) {
			Log.addToDebug(3, "ScanForHubs", "processHubAnswer", "Hub bekannt IP: " + address.getHostAddress());
			hub = this.hubAddresses.get(address);
			//hub.setMasterAddress(ip);
			this.foundHubs.add(hub);
		} else {
			Log.addToDebug(3, "ScanForHubs", "processHubAnswer", "Hub neu IP: " + address.getHostAddress());
			hub = new Hub(this.surface,this.surface.getNextHubNumber(),address, data[2]);
			this.surface.addHub(hub);
		}
		hub.processBroadcastAnswer(data[2], data[1]);
	}
	
	private void cleanUp() {
		//TODO
		/*
		for(Iterator<Hub> i = this.foundHubs.iterator(); i.hasNext(); ) {
			this.oldHubs.remove(i.next());
		}
		for(Iterator<Hub> i = this.oldHubs.iterator(); i.hasNext(); ) {
			this.surface.removeHub(i.next());
		}*/	
	}
	
	private int[] calculateChecksum(byte[] in, int length) {
		int[] out = new int[length-1];
		int i;
		int sum = 0;
		for(i = 0; i < length-1; i++){
			out[i] = (int) (in[i] & 0xFF);
			sum += out[i];
		}
		sum = sum & 0xFF;
		if(sum == (in[length-1] & 0xFF)) {
			this.checkOK = true;
		} else {
			/*
			Log.addToErrorLog("[ScanForHub]{calculateChecksum} -- checksum failure Chacksum is: " + sum + " but should be: " + (in[length-1] & 0xFF));
			*/
			//TODO DEBUG change to false
			this.checkOK = true;
		}	
		return out;
	}
}
